/**
 * Copyright 2018 人人开源 http://www.renren.io
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package io.renren.interceptor;


import com.auth0.jwt.JWTExpiredException;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.JWTVerifyException;
import io.renren.annotation.Login;
import io.renren.dao.ApplicationDao;
import io.renren.entity.Application;
import io.renren.vo.RespModel;
import io.renren.entity.UserInfo;
import io.renren.exception.ResponseModelException;
import io.renren.service.UserService;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

/**
 * 权限(Token)验证
 * @author chenshun
 * @email sunlightcs@gmail.com
 * @date 2017-03-23 15:38
 */
@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private UserService userService;

    @Autowired
    private ApplicationDao applicationDao;

    public static final String USER_KEY = "userId";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Login annotation;
        if(handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(Login.class);
        }else{
            return true;
        }

        if(annotation == null){
            return true;
        }

        //从header中获取token
        String token = request.getHeader("authtoken");
        //如果header中不存在token，则从参数中获取token
        if(StringUtils.isBlank(token)){
            token = request.getParameter("authtoken");
        }

        Map pathVariables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        String applicationId = (String) pathVariables.get("applicationId");

        if (StringUtils.isBlank(applicationId)) {
            throw new ResponseModelException("failed", "405", "NO SUCH APPLICATION", null);
        }

        try {
            // 查询appliaction,获得secret key
            Application application = applicationDao.findOne(applicationId);
            if (application == null) {
                throw new ResponseModelException("failed", "405", "NO SUCH APPLICATION", null);
            }
            // 解析token
            byte[] secret = Base64.decodeBase64(application.getApplicationSecret());
            Map<String, Object> decodedPayload = new JWTVerifier(secret).verify(token);
            // 查询用户信息
            String username = (String) decodedPayload.get("sub");

            RespModel<UserInfo> info = userService.getInfo(username);

            if("failed".equals(info.getStatus())){
                throw new ResponseModelException(info.getStatus(),info.getErrorcode(),info.getReason(),info.getResult());
            }
            //设置userId到request里，后续根据userId，获取用户信息
            request.setAttribute(USER_KEY, info.getResult().getId());
            request.setAttribute("username",info.getResult().getUsername());
            return true;
        } catch (java.security.SignatureException e) {// 签名不合法
            throw new ResponseModelException("failed", "403", "NOT AUTHORIZED", null);
        } catch (JWTExpiredException e) { // 签名过期
            throw new ResponseModelException("failed", "401", "TOKEN EXPIRED", null);
        } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException | JWTVerifyException e) {
            throw new ResponseModelException("failed", "403", "NOT AUTHORIZED", null);
        }
    }
}
